package com.ys.servers.points.service.impl;

import com.ys.servers.common.data.ReturnData;
import com.ys.servers.common.data.UserType;
import com.ys.servers.common.util.CommonUtils;
import com.ys.servers.common.util.TimeUtils;
import com.ys.servers.points.dao.PointsTypeDao;
import com.ys.servers.points.dao.UserPointsDataDao;
import com.ys.servers.points.dao.UserPointsLogDao;
import com.ys.servers.points.dao.UserSignInfoDao;
import com.ys.servers.points.dao.impl.PointsTypeDaoImpl;
import com.ys.servers.points.dao.impl.UserPointsDataDaoImpl;
import com.ys.servers.points.dao.impl.UserPointsLogDaoImpl;
import com.ys.servers.points.dao.impl.UserSignInfoDaoImpl;
import com.ys.servers.points.data.*;
import com.ys.servers.points.service.UserPointsService;

import java.util.List;

import java.util.Map;
import java.util.HashMap;

import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;

/**
 * Created by taotao on 2017/5/10.
 */
public class UserPointsServiceImpl implements UserPointsService {

    private UserPointsDataDao userPointsDataDao;

    private PointsTypeDao pointsTypeDao;

    private UserPointsLogDao userPointsLogDao;

    private UserSignInfoDao userSignInfoDao;

    public UserSignInfoDao getUserSignInfoDao() {
        return userSignInfoDao;
    }

    public UserPointsServiceImpl setUserSignInfoDao(UserSignInfoDao userSignInfoDao) {
        this.userSignInfoDao = userSignInfoDao;
        return this;
    }

    public UserPointsLogDao getUserPointsLogDao() {
        return userPointsLogDao;
    }

    public UserPointsServiceImpl setUserPointsLogDao(UserPointsLogDao userPointsLogDao) {
        this.userPointsLogDao = userPointsLogDao;
        return this;
    }

    public PointsTypeDao getPointsTypeDao() {
        return pointsTypeDao;
    }

    public UserPointsServiceImpl setPointsTypeDao(PointsTypeDao pointsTypeDao) {
        this.pointsTypeDao = pointsTypeDao;
        return this;
    }

    public UserPointsDataDao getUserPointsDataDao() {
        return userPointsDataDao;
    }

    public UserPointsServiceImpl setUserPointsDataDao(UserPointsDataDao userPointsDataDao) {
        this.userPointsDataDao = userPointsDataDao;
        return this;
    }

    public UserPointsServiceImpl() {
        setUserPointsDataDao(new UserPointsDataDaoImpl())
                .setPointsTypeDao(new PointsTypeDaoImpl())
                .setUserPointsLogDao(new UserPointsLogDaoImpl())
                .setUserSignInfoDao(new UserSignInfoDaoImpl());
    }

    @Override
    public UserPointsService addUserPoints(long userId, UserType userType, String pointTypeId, Handler<AsyncResult<ReturnData>> resultHandler) {
        ReturnData returnData = new ReturnData();
        // Map<String, Object> returnMessage = new HashMap<String, Object>();
        //  returnMessage.put("pointsId", pointTypeId);
        // returnMessage.put("suc", false);
        // returnMessage.put("state", UserPointsState.OK);

        Future<ReturnData> future = Future.future();
        //future.setHandler(resultHandler);
        getPointsTypeDao().getPointsType(pointTypeId).setHandler(pointsTypeAsyncResult -> {
            if (CommonUtils.validAsyncResult(pointsTypeAsyncResult)) {
                PointsType pointsType = pointsTypeAsyncResult.result();
                getUserPointsDataDao().getUserPointsData(userId, userType).setHandler(res -> {
                    UserPointsData userPointsData;
                    if (CommonUtils.validAsyncResult(res)) {
                        userPointsData = res.result();
                    } else {
                        userPointsData = new UserPointsData().setUserId(userId).setUserType(userType);
                    }
                    if (userPointsData.isDoneType(pointTypeId)) {
                        handleDonePoints(pointTypeId, pointsType.getLogDesc(), resultHandler);
                        return;
                    }
                    Map<String, Object> returnMessage = new HashMap<String, Object>();
                    returnMessage = getNewUserPointsState(pointsType, userPointsData);
                    returnData.setData(returnMessage);
                    int points = (int) (returnMessage.getOrDefault("doAddPoints", 0));
                    if (points > 0 || userPointsData.isDoneType(pointTypeId)) {
                        getUserPointsDataDao().saveUserPointsData(userPointsData).setHandler(userPointsDataAsyncResult -> {
                            if (CommonUtils.validAsyncResult(userPointsDataAsyncResult)) {

                                UserPointsLog userPointsLog = new UserPointsLog().setLogDesc(pointsType.getLogDesc())
                                        .setUserId(userId).setUserType(userType).setPoint(points).setPointTypeId(Long.parseLong(pointTypeId))
                                        .setTime(System.currentTimeMillis());
                                getUserPointsLogDao().addUserPointsLog(userPointsLog);
                                getUserPointsLogDao().addUserPointsLogToRedisList(30, userPointsLog);
                                getUserPointsLogDao().addUserPointsLogToRedisList(60, userPointsLog);
                                future.setHandler(resultHandler).complete(returnData);
                            } else
                                handleFailAddPoints(pointTypeId, pointsType.getLogDesc(), resultHandler);
                        });
                    } else
                        future.setHandler(resultHandler).complete(returnData);
                });
            } else
                handleFailAddPoints(pointTypeId, "", resultHandler);

        });
        return this;
    }

    private UserPointsService handleFailAddPoints(String pointsTypeId, String logDesc, Handler<AsyncResult<ReturnData>> resultHandler) {
        Future<ReturnData> future = Future.future();
        ReturnData returnData = new ReturnData();
        Map<String, Object> failMessage = new HashMap<String, Object>();
        failMessage.put("suc", false);
        failMessage.put("pointsId", pointsTypeId);
        failMessage.put("state", UserPointsState.OK);
        failMessage.put("doAddPoints", 0);
        failMessage.put("title", logDesc);
        returnData.setData(failMessage);
        future.setHandler(resultHandler).complete(returnData);
        return this;
    }

    private UserPointsService handleDonePoints(String pointsTypeId, String logDesc, Handler<AsyncResult<ReturnData>> resultHandler) {
        Future<ReturnData> future = Future.future();
        ReturnData returnData = new ReturnData();
        Map<String, Object> failMessage = new HashMap<String, Object>();
        failMessage.put("suc", false);
        failMessage.put("pointsId", pointsTypeId);
        failMessage.put("state", UserPointsState.DONE);
        failMessage.put("doAddPoints", 0);
        failMessage.put("title", logDesc);
        returnData.setData(failMessage);
        future.setHandler(resultHandler).complete(returnData);
        return this;
    }

    @Override
    public UserPointsService getRecentUserPointsLog(int days,int limit,int offset, long userId, UserType userType, Handler<AsyncResult<Map>> resultHandler) {

        Future<Map> result = Future.future();
        result.setHandler(resultHandler);
        Map<String, Object> userPointsInfo = new HashMap<>();
        getUserPointsDataDao().getUserPointsData(userId, userType).setHandler(res -> {
            //Map<String, Object> userPointsInfo = new HashMap<String, Object>();
            userPointsInfo.put("totalPoints", 0);
            userPointsInfo.put("currentPoints", 0);
            userPointsInfo.put("level", 0);
            userPointsInfo.put("logs", "");
            userPointsInfo.put("userId", userId);
            UserPointsData userPointsData;
            if (CommonUtils.validAsyncResult(res)) {
                userPointsData = res.result();
                userPointsInfo.put("totalPoints", userPointsData.getTotalPoints());
                userPointsInfo.put("currentPoints", userPointsData.getCurrentPoints());
                userPointsInfo.put("level", userPointsData.getUserLevel());
                getUserPointsLogDao().getRecentlyPointsType(days, limit,offset,Long.toString(userId), userType).setHandler(logsRes -> {
                    if (CommonUtils.validAsyncResult(res)) {
                        userPointsInfo.put("logs", logsRes.result());
                    }
                    result.complete(userPointsInfo);
                });
            } else {
                userPointsData = new UserPointsData().setUserId(userId).setUserType(userType);
                getUserPointsDataDao().saveUserPointsData(userPointsData);
                result.complete(userPointsInfo);
            }
        });


        return this;
    }


    private Map<String, Object> getNewUserPointsState(PointsType pointsType, UserPointsData userPointsData) {

        String pointTypeId = pointsType.getId();

        Map<String, Object> result = new HashMap<String, Object>();
        result.put("pointsId", pointsType.getId());
        result.put("title", pointsType.getLogDesc());

        PointsLoopType loopType = pointsType.getLoopType();

        //默认非循环方式，不需要重置
        boolean shouldReset = false;
        long lastUpdateTime = userPointsData.getLastUpdate(pointTypeId);
        int currentTimelyCount = userPointsData.getTimelyCount(pointTypeId);

        //LoopTypeUnit loopU
        if (loopType.equals(PointsLoopType.TIME_LOOP)) {
            switch (pointsType.getLoopUnit()) {
                case DAILY_LOOP:
                    shouldReset = !(TimeUtils.today(lastUpdateTime));
                    break;
                case WEEKLY_LOOP:
                    shouldReset = !(TimeUtils.thisWeek(lastUpdateTime));
                    break;
                case MONTHLY_LOOP:
                    shouldReset = !(TimeUtils.thisMonth(lastUpdateTime));
                    break;
                default:
                    break;
            }
        } else if (loopType.equals(PointsLoopType.CONTINUOUS_LOOP)) {
            //今天已经领取过了
            if (TimeUtils.today(lastUpdateTime)) {
                result.put("suc", false);
                result.put("state", UserPointsState.DAILY_DONE);
                result.put("doAddPoints", 0);
                return result;
            }
            if (currentTimelyCount >= pointsType.getMaxTime())
                shouldReset = true;
            else if (TimeUtils.continuousDay(lastUpdateTime)) {
                shouldReset = false;
            } else {
                shouldReset = true;
            }
        }

        if (shouldReset) {
            //pointsType
        }

        int points = 0;

        switch (loopType) {
            //处理所有非循环类型积分
            case NO_LOOP:
                if (currentTimelyCount < pointsType.getMaxTime()) {
                    points = pointsType.caculatePoint(currentTimelyCount);
                    userPointsData.doAddPoints(pointTypeId, points, shouldReset);
                    result.put("suc", true);
                    if (currentTimelyCount == pointsType.getMaxTime() - 1) {
                        result.put("state", UserPointsState.DONE);
                        userPointsData.addToDoneList(pointTypeId);
                    } else
                        result.put("state", UserPointsState.OK);
                    result.put("doAddPoints", points);

                } else {
                    userPointsData.addToDoneList(pointTypeId);
                    result.put("suc", false);
                    result.put("state", UserPointsState.DONE);
                    result.put("doAddPoints", 0);
                }
                break;
            // 处理所有循环类型积分

            default:

                switch (loopType) {
                    case TIME_LOOP:
                        if (!shouldReset && (currentTimelyCount == pointsType.getMaxTime())) {
                            result.put("state", UserPointsState.DAILY_DONE);
                        } else if (currentTimelyCount == pointsType.getMaxTime() - 1) {
                            switch (pointsType.getLoopUnit()) {
                                case MONTHLY_LOOP:
                                    result.put("state", UserPointsState.MONTHLY_DONE);
                                case WEEKLY_LOOP:
                                    result.put("state", UserPointsState.WEEKLY_DONE);
                                default:
                                    result.put("state", UserPointsState.DAILY_DONE);
                            }
                        } else
                            result.put("state", UserPointsState.OK);
                        break;
                    case CONTINUOUS_LOOP:
                        result.put("state", UserPointsState.DAILY_DONE);
                        break;
                }

                //case TIME_LOOP:
                //case CONTINUOUS_LOOP:
                if (shouldReset) {
                    points = pointsType.caculatePoint(0);
                } else if (currentTimelyCount < pointsType.getMaxTime()) {
                    points = pointsType.caculatePoint(currentTimelyCount);
                } else {
                    points = 0;
                }
                result.put("doAddPoints", points);
                if (points > 0) {
                    userPointsData.doAddPoints(pointTypeId, points, shouldReset);
                    result.put("suc", true);
                } else {
                    result.put("suc", false);
                }
                //default:
                //   break;
        }


        return result;
    }

    @Override
    public UserPointsService clearUserPoints(String userId, UserType userType, String pointTypeId, Handler<AsyncResult<JsonObject>> resultHandler) {
        JsonObject returnData = new JsonObject();
        Future<JsonObject> future = Future.future();
        //future.setHandler(resultHandler);
        getPointsTypeDao().getPointsType(pointTypeId).setHandler(pointsTypeAsyncResult -> {
            if (CommonUtils.validAsyncResult(pointsTypeAsyncResult)) {
                PointsType pointsType = pointsTypeAsyncResult.result();
                getUserPointsDataDao().getUserPointsData(Long.parseLong(userId), userType).setHandler(res -> {
                    if (CommonUtils.validAsyncResult(res)) {
                        UserPointsData userPointsData = res.result();
                        List<String> doneTypeIds = userPointsData.getDoneTypesId();
                        if (doneTypeIds.contains(pointTypeId)) {
                            doneTypeIds.remove(pointTypeId);
                        }

                        int points = userPointsData.getTotalPoints(pointTypeId);
                        if (points >= 0) {
                            userPointsData.getLastUpdateMap().put(pointTypeId, 0L);
                            userPointsData.getTimelyCountMap().put(pointTypeId, 0);
                            userPointsData.getTotalPointsMap().put(pointTypeId, 0);
                            userPointsData.getTotalCountMap().put(pointTypeId, 0);
                            userPointsData.setCurrentPoints(userPointsData.getCurrentPoints() - points);
                            userPointsData.setTotalPoints(userPointsData.getTotalPoints() - points);
                            //  userPointsData.getTimelyCountMap().put(pointTypeId, 0);
                            getUserPointsDataDao().saveUserPointsData(userPointsData).setHandler(userPointsDataAsyncResult -> {
                                if (CommonUtils.validAsyncResult(userPointsDataAsyncResult)) {
                                    UserPointsLog userPointsLog = new UserPointsLog().setLogDesc("测试手动清零")
                                            .setUserId(Long.parseLong(userId)).setUserType(userType).setPoint(0 - points).setPointTypeId(Long.parseLong(pointTypeId))
                                            .setTime(System.currentTimeMillis());
                                    getUserPointsLogDao().addUserPointsLog(userPointsLog);
                                    getUserPointsLogDao().addUserPointsLogToRedisList(30, userPointsLog);
                                    getUserPointsLogDao().addUserPointsLogToRedisList(60, userPointsLog);

                                    if (pointsType.getId().equals("3")) {
                                        getUserSignInfoDao().userSign(new UserSignInfo().setLastSignInTime(0).setUserType(userType).setUserId(userId).setContinuousDay(0));
                                    }
                                    // future.setHandler(resultHandler).complete(returnData);
                                    returnData.put("suc", true);
                                    returnData.put("clearPoints", points);
                                    returnData.put("message", "用户 " + userId + " , 类型 " + pointTypeId + " 的积分已经清零 。");
                                } else {
                                    returnData.put("suc", false);
                                    //returnData.put("clearPoints", points);
                                    returnData.put("clearPoints", points);
                                    returnData.put("message", "用户 " + userId + " , 类型 " + pointTypeId + " 的积分清零失败 。");
                                }
                                future.setHandler(resultHandler).complete(returnData);
                            });
                        } else {
                            returnData.put("suc", false);
                            returnData.put("message", "用户 " + userId + " , 没有类型 " + pointTypeId + " 的积分 。");
                            future.setHandler(resultHandler).complete(returnData);
                        }
                    } else {
                        returnData.put("suc", false);
                        returnData.put("message", "用户 " + userId + " , 没有类型 " + pointTypeId + " 的积分 。");
                        future.setHandler(resultHandler).complete(returnData);
                    }
                });
            } else {

                returnData.put("suc", false);
                returnData.put("message", "积分类型不存在");
                future.setHandler(resultHandler).complete(returnData);
            }
        });

        return this;
    }

}
